<?php

namespace app\controllers;

use app\models\GuidePayLog;
use Yii;
use app\models\GuideFieldData;
use app\models\GuidePrice;
use app\models\GuideFieldDataQuery;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * GuideController implements the CRUD actions for GuideFieldData model.
 */
class GuideController extends Controller
{

    public function beforeAction($action)
    {
        if($action->id === 'notify'){
            return true;
            // yii 微信 您提交的数据无法被验证。局部关闭。
            //$this->enableCsrfValidation = false;
        }
        return parent::beforeAction($action);
    }

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                //'only' => ['logout'],
                'except' => ['view', 'pay', 'notify'],
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
            'verbs'  => [
                'class'   => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    public function actionUpdatePrice()
    {
        $gid = Yii::$app->request->post('gid');
        $langcode = Yii::$app->request->post('langcode');
        $saved = 0;
        if (($model = GuidePrice::findOne(['gid' => $gid, 'langcode' => $langcode])) === null) {
            $model = new GuidePrice();
            $model->setAttributes([
                'gid'      => $gid,
                'langcode' => $langcode
            ]);
        }
        $model->price = Yii::$app->request->post('price');
        $model->update_at = date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
        if ($model->save()) {
            $saved = 1;
        } else {
            $saved = $model->getErrors();
        }

        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
        return ['saved' => $saved];
    }

    /**
     * Lists all GuideFieldData models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new GuideFieldDataQuery();
        $dataProvider = $searchModel->search(array_merge_recursive(
            Yii::$app->request->queryParams,
            ['GuideFieldDataQuery' => ['parent' => 73]]
        ));

        return $this->render('index', [
            'searchModel'  => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * 支付页面
     * @param string $gid
     * @param string $langcode
     * @return mixed
     */
    public function actionPay()
    {
        $gid = isset($_GET['gid']) ? $_GET['gid'] : 0;
        $langcode = isset($_GET['langcode']) ? $_GET['langcode'] : 0;

        if(!$gid || !$langcode){
            exit('param miss!');
        }

        // weixin pay flow
        $mchid = '1517375291';          //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
        $appid = 'wxa05550e093c4336a';  //微信支付申请对应的公众号的APPID
        $appKey = '8c6d101e7bb60d41e4ecd1e66f9c583c';   //微信支付申请对应的公众号的APP Key
        $apiKey = 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASC';   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥

        // 要求先关注公众号
        //①、获取用户openid
        $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey);
        // 如果之前session中存入了openId，则直接返回
        if (isset($_SESSION['openId'])){
            $openId = $_SESSION['openId'];
        } else {
            $_SESSION['openId'] = $openId = $wxPay->GetOpenid();      //获取openid
        }
        if(!$openId) exit('获取openid失败');

        $userInfo = $wxPay->getUserInfo($openId);

        if (empty($userInfo['subscribe'])) {
            header('location: qrcode-cqkzyzbwg.jpg');
            exit('请先关注公众号！');
            return $this->renderPartial('subscribe');
        }
// var_export($userInfo);exit;
// array ( 'subscribe' => 1, 'openid' => 'oMUqxw-AudWhySiXWWuXQQrK9xUU', 'nickname' => '闃垮北', 'sex' => 1, 'language' => 'zh_CN', 'city' => '', 'province' => '', 'country' => '鐩村竷缃楅檧', 'headimgurl' => 'http://thirdwx.qlogo.cn/mmopen/Q3auHgzwzM7eOxm2GiaK9PpnaiczoNBbo3j0jFJHLM9po6NdQSF1ibmnJHWBvY7icrJccVWQCZXMOZBDyLWmPPVNibnYKXbyicL3AsWr48JYTXlFQ/132', 'subscribe_time' => 1552093010, 'remark' => '', 'groupid' => 0, 'tagid_list' => array ( ), 'subscribe_scene' => 'ADD_SCENE_QR_CODE', 'qr_scene' => 0, 'qr_scene_str' => '', )
        /*
        $user = WechatUser::findUserByOpenId($openId);
        if (!$user) {
            $user = new WechatUser();
            $user->setAttributes($userInfo);
            $user->save();
        }
        */
        
        // ->step 1: in guide page
        $url = 'http://life258.cn/' . (empty($_GET['url']) ? 'guide/' . $gid : $_GET['url']);
        $guide = GuideFieldData::findOne(['gid' => $gid, 'langcode' => $langcode]);
        if(!($price = $guide->getPrice()) || ($price = $guide->getPrice()->price) <= 0){
            header("location:$url");
            exit;
        }

        // step 2. pay;go back with pay-code
        $randKey = isset($_GET['randKey']) ? $_GET['randKey'] : 'why no randKey';
        $k = "pay_{$gid}_$langcode";
        $code = $this->code($randKey, $k);

        // query pay status of gid-langcode
        if (GuidePayLog::findOne(['gid' => $gid, 'langcode' => $langcode, 'uid' => $openId])) {
            header("location:$url?code=$code");
            //echo "<a href=\"?r=guide/view&gid=$gid&langcode=$langcode&code=$code\">go</a>";
            exit;
        }

        //②、统一下单
        $outTradeNo = uniqid();     //你自己的商品订单号
        $payAmount = $price;          //付款金额，单位:元
        $orderName = '查看导游：' . $guide->title . $gid . $langcode;    //订单标题
        $notifyUrl = 'http://life258.cqkangzhan.com/notify.php';     //付款成功后的回调地址(不要有问号)
        $payTime = time();      //付款时间
        $attach = "{$gid}_$langcode"; //原样返回
        $jsApiParameters = $wxPay->createJsBizPackage($openId,$payAmount,$outTradeNo,$orderName,$notifyUrl,$payTime,$attach);
        $jsApiParameters = json_encode($jsApiParameters);

        return $this->renderPartial('pay', [
            'title' => $guide->title,
            'payAmount' => $payAmount,
            'jsApiParameters' => $jsApiParameters,
        ]);
    }

    /**
     * jsapi ajax 查询当前支付的结果（callback获得的结果）
     */
    public function actionPayResult()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

        $gid = isset($_GET['gid']) ? $_GET['gid'] : 0;
        $langcode = isset($_GET['langcode']) ? $_GET['langcode'] : 0;

        if(!$gid || !$langcode){
            return ['error' => 'param miss!'];
        }

        return ['payed' => GuidePayLog::findOne(['gid' => $gid, 'langcode' => $langcode, 'uid' => $uid]) ? 1 : 0];
    }

    /**
     * 静静等待 接收微信回调
     */
    public function actionNotify()
    {
        $mchid = '1517375291';          //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
        $appid = 'wxa05550e093c4336a';  //微信支付申请对应的公众号的APPID
        $apiKey = 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASC';   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥

        $wxPay = new WxpayService($mchid,$appid,'no-need-appKey', $apiKey);
        $result = $wxPay->notify();
        if($result){
            //file_put_contents('../../logs/pay.log', date('Y-m-d H:i:s').var_export($result, 1));
            Yii::info($result, "pay");
            //例如连接数据库，获取付款金额$result['cash_fee']，获取订单号$result['out_trade_no']，修改数据库中的订单状态等;
            list($gid, $langcode) = explode('_', $result['attach']);
            $guidePayLog = new GuidePayLog();
            $guidePayLog->setAttributes([
                'gid'       => $gid,
                'langcode'  => $langcode,
                'price'     => round($result['total_fee'] / 100, 2),
                'uid'       => $result['openid'],
                'create_at' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']),
            ]);
            $guidePayLog->save();
            echo 'pay success!';
            //完成你的逻辑
        }else{
            echo 'pay error';
        }
        exit;
    }

    private function code($randKey, $id)
    {
        $secret = '-$%^&*IJ8G&IH-ui76' . $id;
        return crypt($secret, $randKey); //这个函数比md5可以少存1个数据
    }

    /**
     * 模拟：查看内容页
     * @param string $gid
     * @param string $langcode
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionView($gid, $langcode)
    {
  function code($randKey, $id)
  {
    $secret = '-$%^&*IJ8G&IH-ui76' . $id;
    return crypt($secret, $randKey);
  }
/*
  //-------------------need pay start
if (guide_is_page()) {
    //move out! fix redeclare
  function code($randKey, $id)
  {
    $secret = '-$%^&*IJ8G&IH-ui76' . $id;
    return crypt($secret, $randKey);
  }
  $gid = $guide->gid->value;
  $langcode = $guide->language()->getId();
  if ($guide->parent->entity->id() == 73) {
    $connection= \Drupal::database();
    $result = $connection->query("SELECT count(*) count FROM {guide_price} WHERE gid=$gid AND langcode='$langcode' AND price>0 LIMIT 1")->fetchField();
    if($result){
      $k = "pay_{$gid}_$langcode";
      // step 3. set pay-code cookie;refresh page
      if (isset($_GET['code'])) { // && $_GET['code'] === code($_COOKIE[$k], $k)
          //$_COOKIE[$k] = $_GET['code'];
          setcookie($k, $_GET['code'], time() + 86400);
          header("location:" . $variables['url']);
          //echo "<a href=\"?r=guide/view&gid=$gid&langcode=$langcode\">payed</a>";
          exit;
      }
      // step 1. set randKey;go to pay page;
      $url = "http://life258.cqkangzhan.com/?r=guide/pay&gid=$gid&langcode=$langcode&url=" . $variables['url'];
      if (empty($_COOKIE[$k])) {
        setcookie($k, ($randKey = substr(md5($_SERVER['REQUEST_TIME']), -16)));
        header("location: $url&randKey=$randKey");
        exit;
      } else {
        $randKey = $_COOKIE[$k];
        if ($_COOKIE[$k] !== code($_COOKIE[$k], $k)) {
            header("location:$url&randKey=$randKey");
            exit;
        }
      }
    }
  }
}
  //-------------------need pay end
  */
        if (GuidePrice::find()->where(['gid' => $gid, 'langcode' => $langcode])->andWhere(['>', 'price', 0])->one()) {
          $k = "pay_{$gid}_$langcode";
          // step 3. set pay-code cookie;refresh page
          if (isset($_GET['code'])) { // && $_GET['code'] === code($_COOKIE[$k], $k)
              //$_COOKIE[$k] = $_GET['code'];
              setcookie($k, $_GET['code'], time() + 86400);
              header("location:" . $variables['url']);
              //echo "<a href=\"?r=guide/view&gid=$gid&langcode=$langcode\">payed</a>";
              exit;
          }
          // step 1. set randKey;go to pay page;
          //$url = "http://life258.cqkangzhan.com/?r=guide/pay&gid=$gid&langcode=$langcode&url=" . $variables['url'];
          $url = "?r=guide/pay&gid=$gid&langcode=$langcode&url=";
          if (empty($_COOKIE[$k])) {
            setcookie($k, ($randKey = substr(md5($_SERVER['REQUEST_TIME']), -16)));
            header("location: $url&randKey=$randKey");
            exit;
          } else {
            $randKey = $_COOKIE[$k];
            if ($_COOKIE[$k] !== code($_COOKIE[$k], $k)) {
                header("location:$url&randKey=$randKey");
                exit;
            }
          }
        }

        return $this->render('view', [
            'model' => $this->findModel($gid, $langcode),
        ]);
    }

    /**
     * Creates a new GuideFieldData model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new GuideFieldData();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'gid' => $model->gid, 'langcode' => $model->langcode]);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

    /**
     * Updates an existing GuideFieldData model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param string $gid
     * @param string $langcode
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionUpdate($gid, $langcode)
    {
        $model = $this->findModel($gid, $langcode);

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'gid' => $model->gid, 'langcode' => $model->langcode]);
        }

        return $this->render('update', [
            'model' => $model,
        ]);
    }

    /**
     * Deletes an existing GuideFieldData model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param string $gid
     * @param string $langcode
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionDelete($gid, $langcode)
    {
        $this->findModel($gid, $langcode)->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the GuideFieldData model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param string $gid
     * @param string $langcode
     * @return GuideFieldData the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($gid, $langcode)
    {
        if (($model = GuideFieldData::findOne(['gid' => $gid, 'langcode' => $langcode])) !== null) {
            return $model;
        }

        throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
    }
}
//测试用
class WxpayServiceFaker
{
    public function __construct($mchid, $appid, $appKey,$key){}
    public function GetOpenid(){return 'openId_cqiu';}
    public function createJsBizPackage(){return [];}
    public function getAccessToken($refresh = false){return 'access_token_string';}
}
class WxpayService
{

    //这里只看原生的几句就行。因为微信授权域名只能设置两个，而有多个系统需要使用，所以做了一层代理，通过代理平台的接口获取access_token。
    //而这个项目比较老，零几年的了，所以redis、memcache之类的都没有可用的，就直接写文件记录了，具体的失效处理就不写了。
    public function getAccessToken($refresh = false)
    {
        // 微信原生方法
        // $get_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->secret";
        // $response = file_get_contents($get_token_url);
        // $tokenInfo = json_decode($response, true);
        // $accessToken = isset($tokenInfo['access_token'])? $tokenInfo['access_token'] : '';

        // 平台接口获取
        // 如果需要刷新token 或 token文件不存在，则重新请求token并写入文件
        $accessTokenFile = '/tmp/access_token';
        if ($refresh || !file_exists($accessTokenFile) || filemtime($accessTokenFile)<$_SERVER['REQUEST_TIME']-7200){
           $accessTokenApi = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appid&secret=$this->appKey";
           $res = file_get_contents($accessTokenApi);
           $tokenInfo = json_decode($res, true);
           $accessToken = isset($tokenInfo['access_token'])? $tokenInfo['access_token'] : '';
           if (!$accessToken) {
               exit('获取accessToken失败！');
           }
           file_put_contents($accessTokenFile, $accessToken);
        }else{// 否则直接取文件
           $accessToken = file_get_contents($accessTokenFile);
        }
        $this->accessToken = $accessToken;
        return $accessToken;
    }
    /*
    {
        "subscribe": 1, 
        "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", 
        "nickname": "Band", 
        "sex": 1, 
        "language": "zh_CN", 
        "city": "广州", 
        "province": "广东", 
        "country": "中国", 
        "headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
        "subscribe_time": 1382694957,
        "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
        "remark": "",
        "groupid": 0,
        "tagid_list":[128,2],
        "subscribe_scene": "ADD_SCENE_QR_CODE",
        "qr_scene": 98765,
        "qr_scene_str": ""
    }
    */
    public function getUserInfo($openId)
    {
        if(!$this->accessToken){
            $this->getAccessToken();
        }
        $get_user_info_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$this->accessToken&openid=$openId&lang=zh_CN";
        $response = file_get_contents($get_user_info_url);
        $info = json_decode($response, true);
        return $info;
    }

    //------

    private $accessToken;

    protected $mchid;
    protected $appid;
    protected $appKey;
    protected $apiKey;
    public $data = null;
    public function __construct($mchid, $appid, $appKey,$key)
    {
        $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
        $this->appid = $appid; //微信支付申请对应的公众号的APPID
        $this->appKey = $appKey; //微信支付申请对应的公众号的APP Key
        $this->apiKey = $key;   //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
    }
    /**
     * 通过跳转获取用户的openid，跳转流程如下：
     * 1、设置自己需要调回的url及其其他参数，跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize
     * 2、微信服务处理完成之后会跳转回用户redirect_uri地址，此时会带上一些参数，如：code
     * @return 用户的openid
     */
    public function GetOpenid()
    {
        //通过code获得openid
        if (!isset($_GET['code'])){
            //触发微信返回code码
            $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on') ? 'https://' : 'http://';
            $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING'];
            if($_SERVER['REQUEST_URI']) $uri = $_SERVER['REQUEST_URI'];
            $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri);
            $url = $this->__CreateOauthUrlForCode($baseUrl);
            Header("Location: $url");
            exit();
        } else {
            //获取code码，以获取openid
            $code = $_GET['code'];
            $openid = $this->getOpenidFromMp($code);
            return $openid;
        }
    }
    /**
     * 通过code从工作平台获取openid机器access_token
     * @param string $code 微信跳转回来带上的code
     * @return openid
     */
    public function GetOpenidFromMp($code)
    {
        $url = $this->__CreateOauthUrlForOpenid($code);
        $res = self::curlGet($url); // 这个url只能取一次，第二次就报错了！
        //取出openid
        $data = json_decode($res,true);
        if(!$data || !isset($data['openid']) || isset($data['errmsg'])){
            exit($res);
        }
        $this->data = $data;
        $openid = $data['openid'];
        return $openid;
    }
    /**
     * 构造获取open和access_toke的url地址
     * @param string $code，微信跳转带回的code
     * @return 请求的url
     */
    private function __CreateOauthUrlForOpenid($code)
    {
        $urlObj["appid"] = $this->appid;
        $urlObj["secret"] = $this->appKey;
        $urlObj["code"] = $code;
        $urlObj["grant_type"] = "authorization_code";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
    }
    /**
     * 构造获取code的url连接
     * @param string $redirectUrl 微信服务器回跳的url，需要url编码
     * @return 返回构造好的url
     */
    private function __CreateOauthUrlForCode($redirectUrl)
    {
        $urlObj["appid"] = $this->appid;
        $urlObj["redirect_uri"] = "$redirectUrl";
        $urlObj["response_type"] = "code";
        $urlObj["scope"] = "snsapi_base";
        $urlObj["state"] = "STATE"."#wechat_redirect";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
    }
    /**
     * 拼接签名字符串
     * @param array $urlObj
     * @return 返回已经拼接好的字符串
     */
    private function ToUrlParams($urlObj)
    {
        $buff = "";
        foreach ($urlObj as $k => $v)
        {
            if($k != "sign") $buff .= $k . "=" . $v . "&";
        }
        $buff = trim($buff, "&");
        return $buff;
    }
    /**
     * 统一下单
     * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid
     * @param float $totalFee 收款总费用 单位元
     * @param string $outTradeNo 唯一的订单号
     * @param string $orderName 订单名称
     * @param string $notifyUrl 支付结果通知url 不要有问号
     * @param string $timestamp 支付时间
     * @param string $attach 附言 原样返回
     * @return string
     */
    public function createJsBizPackage($openid, $totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp, $attach='pay')
    {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->apiKey,
        );
        //$orderName = iconv('GBK','UTF-8',$orderName);
        $unified = array(
            'appid' => $config['appid'],
            'attach' => $attach,             //商家数据包，原样返回，如果填写中文，请注意转换为utf-8
            'body' => $orderName,
            'mch_id' => $config['mch_id'],
            'nonce_str' => self::createNonceStr(),
            'notify_url' => $notifyUrl,
            'openid' => $openid,            //rade_type=JSAPI，此参数必传
            'out_trade_no' => $outTradeNo,
            'spbill_create_ip' => '127.0.0.1',
            'total_fee' => intval($totalFee * 100),       //单位 转为分
            'trade_type' => 'JSAPI',
        );
        $unified['sign'] = self::getSign($unified, $config['key']);
        $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);     
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($unifiedOrder === false) {
            die('parse xml error');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            die($unifiedOrder->return_msg);
        }
        if ($unifiedOrder->result_code != 'SUCCESS') {
            die($unifiedOrder->err_code);
        }
        $arr = array(
            "appId" => $config['appid'],
            "timeStamp" => "$timestamp",        //这里是字符串的时间戳，不是int，所以需加引号
            "nonceStr" => self::createNonceStr(),
            "package" => "prepay_id=" . $unifiedOrder->prepay_id,
            "signType" => 'MD5',
        );
        $arr['paySign'] = self::getSign($arr, $config['key']);
        return $arr;
    }
    public static function curlGet($url = '', $options = array())
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    public static function curlPost($url = '', $postData = '', $options = array())
    {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    public static function createNonceStr($length = 16)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    public static function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }
    public static function getSign($params, $key)
    {
        ksort($params, SORT_STRING);
        $unSignParaString = self::formatQueryParaMap($params, false);
        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
        return $signStr;
    }
    protected static function formatQueryParaMap($paraMap, $urlEncode = false)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if (null != $v && "null" != $v) {
                if ($urlEncode) {
                    $v = urlencode($v);
                }
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

    //-------notify
    public function notify()
    {
        $config = array(
            'mch_id' => $this->mchid,
            'appid' => $this->appid,
            'key' => $this->apiKey,
        );
        $postStr = file_get_contents('php://input');
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);        
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($postObj === false) {
            die('parse xml error');
        }
        if ($postObj->return_code != 'SUCCESS') {
            die($postObj->return_msg);
        }
        if ($postObj->result_code != 'SUCCESS') {
            die($postObj->err_code);
        }
        $arr = (array)$postObj;
        unset($arr['sign']);
        if (self::getSign($arr, $config['key']) == $postObj->sign) {
            echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            return $arr;
        }
    }
}
